package logik;

import interfaces.ISteuerung;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.imageio.ImageIO;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.swing.JPanel;
import javax.swing.ListModel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import lib.Konstanten;
import lib.Propertie;
import mailHelper.EmailHelper;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import vo.TOGui;
import druckkomponente.PDFPrinter;

public class Datenverarbeitung implements ISteuerung {
	private static Datenverarbeitung datenverarbeitungsInstanz;
	private FopFactory fopFactory = FopFactory.newInstance();
	Propertie propertie;

	public Datenverarbeitung() throws InvalidFormatException, IOException {
		propertie = Propertie.ladepropertieInstanz();
	}

	public static Datenverarbeitung ladedatenverarbeitungsInstanz()
			throws InvalidFormatException, IOException {
		if (datenverarbeitungsInstanz == null) {
			datenverarbeitungsInstanz = new Datenverarbeitung();
		}
		return datenverarbeitungsInstanz;
	}

	public boolean bauePDF(TOGui vo) {
		try {
			baueXML(vo);
			erstelleBild(vo.getUnterschriftMonteur(), "UnterschriftMonteur");
			erstelleBild(vo.getUnterschriftKunde(), "UnterschriftKunde");
			baueFOP();

			// Setup directories
			File baseDir = new File(Konstanten.TEMPPDFPFAD);
			File outDir = new File(vo.getPfad() + "/");

			// Setup input and output files
			File fofile = new File(baseDir, "foFile.fo");
			File pdffile = new File(outDir, vo.getPdfName());
			konvertiereVonFOZuPDF(fofile, pdffile);
		} catch (Exception e) {
			return false;
		} finally {
			bereinigeTemp();
		}

		return true;

	}

	private void bereinigeTemp() {

		File rootfile = new File(Konstanten.TEMPPDFPFAD);
		File[] files = rootfile.listFiles();
		if (files != null) {
			for (File file : files) {
				if (!file.isDirectory()) {
					file.delete();
				}
			}
		}
	}

	private void baueFOP() throws IOException, TransformerException {
		// Setup directories
		File baseDir = new File(Konstanten.TEMPPDFPFAD);

		// Setup input and output files
		File xsltfile = new File(Konstanten.PDFVORLAGE);
		File xmlfile = new File(baseDir, "xmlFile.xml");

		File fofile = new File(baseDir, "foFile.fo");

		konvertiereXMLZuFO(xmlfile, xsltfile, fofile);
	}

	private void konvertiereXMLZuFO(File xml, File xslt, File fo)
			throws IOException, TransformerException {

		// Setup output
		OutputStream out = new java.io.FileOutputStream(fo);
		try {
			// Setup XSLT
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer(new StreamSource(
					xslt));
			// Setup input for XSLT transformation
			Source src = new StreamSource(xml);
			// Resulting SAX events (the generated FO) must be piped through to
			// FOP
			Result res = new StreamResult(out);
			// Start XSLT transformation and FOP processing
			transformer.transform(src, res);
		} finally {
			out.close();
		}
	}

	private void baueXML(TOGui vo) throws Exception {
			// Ein neues Dokument wird erstellt und der root-Tag erstellt
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();

			Element root = doc.createElement("root");
			doc.appendChild(root);

			// Pfad Logo
			Element logoNode = doc.createElement("logo");
			Text logoPfad = doc.createTextNode(Konstanten.LOGOPFAD
					+ propertie.getPropertieEintrag("LOGONAME"));
			logoNode.appendChild(logoPfad);
			root.appendChild(logoNode);

			// Pfad Monteur
			Element untMonteurNode = doc.createElement("untMonteur");
			Text untMonteurPfad = doc.createTextNode(Konstanten.TEMPPDFPFAD
					+ "\\UnterschriftMonteur.png");
			untMonteurNode.appendChild(untMonteurPfad);
			root.appendChild(untMonteurNode);

			// Pfad Kunde
			Element untKundeNode = doc.createElement("untKunde");
			Text untKundePfad = doc.createTextNode(Konstanten.TEMPPDFPFAD
					+ "\\UnterschriftKunde.png");
			untKundeNode.appendChild(untKundePfad);
			root.appendChild(untKundeNode);

			// BGA
			Element bgaNode = doc.createElement("bga");
			Text bgaText = doc.createTextNode(vo.getBga());
			bgaNode.appendChild(bgaText);
			root.appendChild(bgaNode);

			// BHKW
			Element bhkwNode = doc.createElement("bhkw");
			Text bhkwText = doc.createTextNode(vo.getBhkw());
			bhkwNode.appendChild(bhkwText);
			root.appendChild(bhkwNode);

			// Ankunft
			Element ankunftNode = doc.createElement("ankunft");
			Text ankunftText = doc.createTextNode(vo.getAnkunftstunde() + ":"
					+ vo.getAnkunftminuten());
			ankunftNode.appendChild(ankunftText);
			root.appendChild(ankunftNode);

			// Abfahrt
			Element abfahrtNode = doc.createElement("abfahrt");
			Text abfahrtText = doc.createTextNode(vo.getAbfahrtstunde() + ":"
					+ vo.getAbfahrtminuten());
			abfahrtNode.appendChild(abfahrtText);
			root.appendChild(abfahrtNode);

			// Datum
			Element datumNode = doc.createElement("datum");
			Text datumText = doc.createTextNode(vo.getDatum());
			datumNode.appendChild(datumText);
			root.appendChild(datumNode);

			// Betriebsstunden
			Element betriebsstundenNode = doc.createElement("betriebsstunden");
			Text betriebsstundenText = doc.createTextNode(vo
					.getBetriebsstunden());
			betriebsstundenNode.appendChild(betriebsstundenText);
			root.appendChild(betriebsstundenNode);

			// Fehler
			Element fehlerNode = doc.createElement("fehler");
			Text fehlerText = doc.createTextNode(vo.getFehler());
			fehlerNode.appendChild(fehlerText);
			root.appendChild(fehlerNode);

			// Arbeiten
			Element ausgefuehrteArbeitenNode = doc
					.createElement("ausgefuehrteArbeiten");
			Text ausgefuehrteArbeitenText = doc
					.createTextNode(vo.getArbeiten());
			ausgefuehrteArbeitenNode.appendChild(ausgefuehrteArbeitenText);
			root.appendChild(ausgefuehrteArbeitenNode);

			// Artikelliste
			Element artikelliste = doc.createElement("artikelliste");
			ListModel<String> list = vo.getArtikelliste();
			for (int i = 0; i < list.getSize(); i++) {
				Element artikelNode = doc.createElement("artikel");
				Text artikelText = doc.createTextNode(list.getElementAt(i));
				artikelNode.appendChild(artikelText);
				artikelliste.appendChild(artikelNode);
			}
			root.appendChild(artikelliste);

			// Monteur
			Element monteurNode = doc.createElement("monteur");
			Text monteurText = doc.createTextNode(vo.getMonteur());
			monteurNode.appendChild(monteurText);
			root.appendChild(monteurNode);

			// Kunde
			Element kundeNode = doc.createElement("kunde");
			Text kundeText = doc.createTextNode(vo.getKunde());
			kundeNode.appendChild(kundeText);
			root.appendChild(kundeNode);

			// Output the XML

			// set up a transformer
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			// trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
//			trans.setOutputProperty(
//					"{http://xml.apache.org/xslt}indent-amount", "2");

			// create string from xml tree
			FileOutputStream outputstream = new FileOutputStream(
					Konstanten.TEMPPDFPFAD + "\\xmlFile.xml");
			StreamResult result = new StreamResult(outputstream);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);

	}

	public void konvertiereVonFOZuPDF(File fo, File pdf) throws Exception {

		OutputStream out = null;

		try {
			FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
			// configure foUserAgent as desired

			// Setup output stream. Note: Using BufferedOutputStream
			// for performance reasons (helpful with FileOutputStreams).
			out = new FileOutputStream(pdf);
			out = new BufferedOutputStream(out);

			// Construct fop with desired output format
			Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent,
					out);

			// Setup JAXP using identity transformer
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer(); // identity
																// transformer

			// Setup input stream
			Source src = new StreamSource(fo);

			// Resulting SAX events (the generated FO) must be piped through to
			// FOP
			Result res = new SAXResult(fop.getDefaultHandler());

			// Start XSLT transformation and FOP processing
			transformer.transform(src, res);

			// Result processing
			// FormattingResults foResults = fop.getResults();
			// for(int i = 0 ; i<foResults.getPageSequences().size();i++){
			// PageSequenceResults pageSequenceResults=(PageSequenceResults)
			// foResults.getPageSequences().get(i);
			// }
		} finally {
			out.close();
		}
	}

	public void versendeMail(String pfadName, String pdfName,
			String empfaenger, String datum) throws AddressException,
			MessagingException, IOException, InvalidFormatException {
		EmailHelper email = new EmailHelper();
		email.sendeMail(pfadName, pdfName, empfaenger, datum);
	}

	public void druckePDF(String pfad, String pdfName) {
		File pfadDesDrucks = new File(pfad + "\\" + pdfName);
		new PDFPrinter(pfadDesDrucks);
	}

	private void erstelleBild(JPanel unterschriftKunde, String name) throws Exception {
			File outputfile = new File(Konstanten.TEMPPDFPFAD + "\\" + name
					+ ".png");
			BufferedImage image = new BufferedImage(
					unterschriftKunde.getWidth(),
					unterschriftKunde.getHeight(), BufferedImage.TYPE_INT_RGB);
			Graphics g = image.getGraphics();
			g.setColor(unterschriftKunde.getBackground());
			g.fillRect(0, 0, image.getWidth(), image.getHeight());
			unterschriftKunde.print(g);
			g.dispose();

			ImageIO.write(image, "png", outputfile);
	}

	public static Datenverarbeitung getDatenverarbeitungsInstanz() {
		return datenverarbeitungsInstanz;
	}

	public static void setDatenverarbeitungsInstanz(
			Datenverarbeitung datenverarbeitungsInstanz) {
		Datenverarbeitung.datenverarbeitungsInstanz = datenverarbeitungsInstanz;
	}

}
